////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//                                                                                                                            //
//                                                         WNProject                                                          //
//                                                                                                                            //
//         This file is distributed under the BSD 2-Clause open source license. See Licenses/License.txt for details.         //
//                                                                                                                            //
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

#include "WNScripting/inc/WNFunctionCallExpression.h"
#include "WNScripting/inc/WNCodeModule.h"
#include "WNScripting/inc/WNTypeManager.h"
#include "WNScripting/inc/WNExpression.h"
#include "WNScripting/inc/WNScriptLinkedList.h"
#include "WNScripting/inc/WNNodeHelpers.h"
#include "WNScripting/inc/WNArgList.h"

#ifdef _WN_MSVC
    #pragma warning(push)
    #pragma warning(disable: 4100)
    #pragma warning(disable: 4127)
    #pragma warning(disable: 4152)
    #pragma warning(disable: 4244)
    #pragma warning(disable: 4245)
    #pragma warning(disable: 4267)
    #pragma warning(disable: 4355)
    #pragma warning(disable: 4512)
#endif

#include "llvm/IR/IRBuilder.h"

#ifdef _WN_MSVC
    #pragma warning(pop)
#endif

using namespace WNScripting;    
WNFunctionCallExpr::WNFunctionCallExpr() :
    mArgs(WN_NULL) {
    
}

WNFunctionCallExpr::WNFunctionCallExpr(WNArgList* _list) :
    mArgs(_list) {
}

WNFunctionCallExpr::~WNFunctionCallExpr() {
    if(mArgs) {
        WN_DELETE(mArgs);
    }
}

eWNTypeError WNFunctionCallExpr::GenerateCode(WNCodeModule& _module, const WNFunctionDefinition* _def, WNLogging::WNLog& _compilationLog) {
    eWNTypeError err = eWNOK;
    WNScriptType voidType;
    if(eWNOK != (err = _module.GetTypeManager().GetTypeByName("Void", voidType))) {
        _compilationLog.Log(WNLogging::eCritical, 0, "Cannot find void type");
        LogLine(_compilationLog, WNLogging::eCritical);
        return(err);
    }
    
    WNScriptType functionType;
    if(eWNOK != (err = mBaseExpression->GenerateCode(_module, _def, _compilationLog))) {
        return(err);
    }
    if(eWNOK != (err = _module.GetTypeManager().GetTypeByName("-Function", functionType))) {
        _compilationLog.Log(WNLogging::eCritical, 0, "Cannot find function type");
        LogLine(_compilationLog, WNLogging::eCritical);
        return(err);
    }

    if(mBaseExpression->GetType() != functionType) {
        _compilationLog.Log(WNLogging::eCritical, 0, "Function is of wrong type");
        LogLine(_compilationLog, WNLogging::eCritical);
        return(eWNInvalidOperation);
    }

    static WNScriptLinkedList<WNExpression> staticList;
    WNScriptLinkedList<WNExpression>& localExpressionList = mArgs? mArgs->GetExpressions() : staticList; 
    std::vector<FunctionParam> scriptTypes;
    for(WNScriptLinkedList<WNExpression>::WNScriptLinkedListNode* i = localExpressionList.first; i != WN_NULL; i = i->next) {
        if(eWNOK != (err = i->value->GenerateCode(_module, _def, _compilationLog))) {
            return(err);
        }
        scriptTypes.push_back(FunctionParam());
        scriptTypes.back().mValue = i->value->GetValue();
        scriptTypes.back().mType = i->value->GetType();
    }
    
    if(eWNOK != (err = GenerateFunctionCall(_module, _def, reinterpret_cast<WN_CHAR*>(mBaseExpression->GetValue()), scriptTypes, true, mScriptType, mValue, _compilationLog))) {
        _compilationLog.Log(WNLogging::eError, 0, "Cannot create function call");
        LogLine(_compilationLog, WNLogging::eError);
        return(err);
    }

    if(mScriptType != voidType) {
        mForceUse = true;
    }
    return(eWNOK);
}

